package com.keya.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.http.HttpUtil;
import com.keya.base.WorkSpace;
import lombok.extern.slf4j.Slf4j;

import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 多线程下载多个文件.
 *
 * @author : xf.he
 * @version : 1.0 2023/6/20
 * @since : 1.0
 **/
@Slf4j
public class MultiThreadDownloadFiles {

    public static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() - 5);

    public static boolean downloadCountDownLatch(WorkSpace workSpace, String[] FILE_URLS) {
        CountDownLatch latch = new CountDownLatch(FILE_URLS.length);
        AtomicLong size = new AtomicLong(0L);
        Set<String> errorFiles = ConcurrentHashMap.newKeySet();
        for (String url : FILE_URLS) {
            executorService.submit(() -> {
                try {
                    long l = HttpUtil.downloadFile(url, workSpace.getFile(), 30000);
                    size.addAndGet(l);
                } catch (Exception e) {
                    String fileName = Paths.get(url).getFileName().toString();
                    errorFiles.add(fileName);
                    log.error(fileName + " 下载失败: " + e.getMessage());
                } finally {
                    latch.countDown();
                }
            });
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            log.error("等待任务完成时被中断", e);
        }
        executorService.shutdown();
        log.info("文件大小为：" + FileUtil.readableFileSize(size.get()));
        if (!errorFiles.isEmpty()) {
            log.info("文件数量为：" + FILE_URLS.length + "失败数量为：" + errorFiles.size());
        }
        return errorFiles.isEmpty();
    }

    /**
     * 下载文件
     */
    public static boolean download(WorkSpace workSpace, String[] FILE_URLS) {
        AtomicLong size = new AtomicLong(0L);
        Set<String> errorFiles = ConcurrentHashMap.newKeySet();
        // 查看默认线程数
        ForkJoinPool.commonPool().getParallelism();
        Arrays.asList(FILE_URLS).parallelStream().forEach(url -> {
            try {
                long l = HttpUtil.downloadFile(url, workSpace.getFile(), 30000);
                size.addAndGet(l);
            } catch (Exception e) {
                String fileName = Paths.get(url).getFileName().toString();
                errorFiles.add(fileName);
                log.error(fileName + " 下载失败: " + e.getMessage());
            }
        });
        log.info("文件大小为：" + FileUtil.readableFileSize(size.get()));
        //少两张应该没啥事
        if (errorFiles.size() > 2) {
            log.info("文件数量为：" + FILE_URLS.length + "失败数量为：" + errorFiles.size());
            return false;
        }
        return true;
    }


    public static void main(String[] args) {
        ExecutorService service = ThreadUtil.newExecutor(4);
        final CountDownLatch cdOrder = new CountDownLatch(1);
        final CountDownLatch cdAnswer = new CountDownLatch(4);
        for (int i = 0; i < 4; i++) {
            Runnable runnable = () -> {
                try {
                    System.out.println("选手" + Thread.currentThread().getName() + "正在等待裁判发布口令");
                    cdOrder.await();
                    System.out.println("选手" + Thread.currentThread().getName() + "已接受裁判口令");
                    Thread.sleep((long) (Math.random() * 10000));
                    System.out.println("选手" + Thread.currentThread().getName() + "到达终点");
                    cdAnswer.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
            service.execute(runnable);
        }
        try {
            Thread.sleep((long) (Math.random() * 10000));
            System.out.println("裁判" + Thread.currentThread().getName() + "即将发布口令");
            cdOrder.countDown();
            System.out.println("裁判" + Thread.currentThread().getName() + "已发送口令，正在等待所有选手到达终点");
            cdAnswer.await();
            System.out.println("所有选手都到达终点");
            System.out.println("裁判" + Thread.currentThread().getName() + "汇总成绩排名");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        service.shutdown();
    }

}
